﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Linq;

using log4net.Config;

using ProjectBase.Core;

using log4net;
using Constants = $safeprojectname$.Publishing.Handlers.Constants;

namespace $safeprojectname$.Publishing
{
    public class Log4NetPublishingHandler : PublishingHandlerBase
    {
        public override void Init(XElement settings)
        {
            var fileName = GetConfigFileName(settings) ?? Constants.DefaultLog4NetConfigPath;
            var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName);

            XmlConfigurator.ConfigureAndWatch(new FileInfo(path));
        }

        public override void Exit()
        {
            LogManager.Shutdown();
            base.Exit();
        }

        #region Publish
        protected override PublishingResult Publish(object reporter, string message, Exception exception)
        {
            if (exception.IsNull())
            {
                return PublishingResult.Continue;
            }
            var type = (reporter ?? exception).GetType();
            var logger = LogManager.GetLogger(type);

            logger.Fatal(message ?? exception.Message, exception);
            return PublishingResult.Continue;
        }

        protected override PublishingResult Publish(object reporter, Severity severity, object message, params object[] args)
        {
            if (message.IsNull())
            {
                return PublishingResult.Continue;
            }

            var type = (reporter ?? message).GetType();
            var logger = LogManager.GetLogger(type);

            switch (severity)
            {
                case Severity.Info:
                    logger.InfoFormat(CultureInfo.InvariantCulture, message.ToString(), args);
                    break;
                case Severity.Debug:
                    logger.DebugFormat(CultureInfo.InvariantCulture, message.ToString(), args);
                    break;
                case Severity.Warning:
                    logger.DebugFormat(CultureInfo.InvariantCulture, message.ToString(), args);
                    break;
                case Severity.Error:
                    logger.ErrorFormat(CultureInfo.InvariantCulture, message.ToString(), args);
                    break;
            }
            return PublishingResult.Continue;
        }

        protected override PublishingResult Publish(object reporter, Func<string> message, Severity severity)
        {
            if (message.IsNull())
            {
                return PublishingResult.Continue;
            }

            var type = (reporter ?? message).GetType();
            var logger = LogManager.GetLogger(type);

            switch (severity)
            {
                case Severity.Info:
                    if (logger.IsInfoEnabled)
                    {
                        logger.Info(TryInvokeMessage(message, logger));
                    }
                    break;
                case Severity.Debug:
                    if (logger.IsDebugEnabled)
                    {
                        logger.Debug(TryInvokeMessage(message, logger));
                    }
                    break;
                case Severity.Warning:
                    if (logger.IsWarnEnabled)
                    {
                        logger.Warn(TryInvokeMessage(message, logger));
                    }
                    break;
                case Severity.Error:
                    if (logger.IsErrorEnabled)
                    {
                        logger.Error(TryInvokeMessage(message, logger));
                    }
                    break;
            }
            return PublishingResult.Continue;
        }
        #endregion Publish


        #region protected Invoke
        protected virtual string TryInvokeMessage(Func<string> message, ILog logger)
        {
            try
            {
                return message.Invoke();
            }
            catch (Exception exception)
            {
                logger.Fatal("Log4Net PublishingHandler: Cannot Invoke() provided Func<string>", exception);
                return exception.Message;
            }
        }

        protected string GetConfigFileName(XElement settings)
        {
            var configPath = settings.Element(Constants.ElementLog4NetConfig);
            if (configPath.IsNull())
            {
                return null;
            }

            var value = configPath.Attribute(Constants.AttributeValue);
            if (value.Is())
            {
                return value.Value;
            }
            return null;
        }
        #endregion protected Invoke
    }
}